//Erstellt von Moritz Liepe
package main;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;

import main.editor.VOSEditor;

import org.eclipse.draw2d.ChopboxAnchor;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.ManhattanConnectionRouter;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.MouseMotionListener;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.IHandlerService;



public class ModuleEditor extends Figure implements MouseListener, MouseMotionListener{

	private int xAdjust = 0, yAdjust = 0;
	private final XYLayout contentsLayout = new XYLayout();
	private final HashMap<UUID, ArrayList<UUID>> connections = new HashMap<UUID, ArrayList<UUID>>();
	private ChopboxAnchor sourceAnc, targetAnc;
	private Figure tempFig = new Figure(), connectionFixPoint = new Figure();
	private int mouseButton = 0;
	private PolylineConnection tempConnection = null;
	
	public ModuleEditor(){
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.setLayoutManager(contentsLayout);
	}
	
	private boolean addConnection(){
		if (sourceAnc !=null && targetAnc !=null){
			PolylineConnection c = new PolylineConnection();
			ManhattanConnectionRouter router = new ManhattanConnectionRouter();
			c.setConnectionRouter(router);
			c.setSourceAnchor(sourceAnc);
			c.setTargetAnchor(targetAnc);
			c.setForegroundColor(ColorConstants.red);			
			if (connections.containsKey( ((IOControl)sourceAnc.getOwner()).getID()) )
			{	
				for(int i = 0; i < connections.get(((IOControl)sourceAnc.getOwner()).getID()).size(); i++)
				{
					if(connections.get(((IOControl)sourceAnc.getOwner()).getID()).get(i).equals(((IOControl)targetAnc.getOwner()).getID())){
						return false;
					}
				}
				connections.get(((IOControl)sourceAnc.getOwner()).getID()).add(((IOControl)targetAnc.getOwner()).getID());
			}
			else {
				ArrayList<UUID> al = new ArrayList<UUID>();
				al.add(((IOControl)targetAnc.getOwner()).getID());
				connections.put(((IOControl)sourceAnc.getOwner()).getID(), al);
			}
			sourceAnc = null;
			targetAnc = null;
			this.add(c);
			IEditorPart page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
			if((page != null) && (page instanceof VOSEditor)) ((VOSEditor) page).setDirty(true);
			return true;
		}
		return false;
	}

	
	@Override
	public void mousePressed(MouseEvent me) {
		if(me.x > 250){
			
			VOSEditor vosEditor = null;
			IEditorPart page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
			if((page != null) && (page instanceof VOSEditor))
			{
				vosEditor = (VOSEditor)page;
				vosEditor.setSelection(StructuredSelection.EMPTY);
				SelectionChangedEvent sce = new SelectionChangedEvent(vosEditor, vosEditor.getSelection());
				for(ISelectionChangedListener selectionChangedListener : vosEditor.getSelectionChangedListenerList())
				{
					selectionChangedListener.selectionChanged(sce);
				}
			}
			
			if (this.findFigureAt(me.x, me.y)!= null && this.findFigureAt(me.x, me.y)!= this){
					tempFig = (Figure)this.findFigureAt(me.x, me.y);
					if(tempFig instanceof IOControl && me.button == 1){
						mouseButton = 1;
						if(((IOControl)tempFig).isConnectable()){
							xAdjust = me.x - tempFig.getLocation().x;
							yAdjust = me.y - tempFig.getLocation().y;
							return;
						}
					}
					else if(tempFig instanceof IOControl && me.button == 3 && ((IOControl)tempFig).getType().equalsIgnoreCase(IModuleControl.TYPE_OUTPUT)){
						mouseButton = 3;
						sourceAnc = new ChopboxAnchor(tempFig);
						this.add(connectionFixPoint);
						contentsLayout.setConstraint(connectionFixPoint, new Rectangle(me.x, me.y, 1, 1));
						return;
					}
					else if(tempFig.getParent() instanceof TextControl && me.button == 3){

						if(vosEditor != null)
						{
							vosEditor.setSelection(new StructuredSelection(new TextControl[]{(TextControl)tempFig.getParent()}));
							SelectionChangedEvent sce = new SelectionChangedEvent(vosEditor, vosEditor.getSelection());
							for(ISelectionChangedListener selectionChangedListener : vosEditor.getSelectionChangedListenerList())
							{
								selectionChangedListener.selectionChanged(sce);
							}
						}
						tempFig = null;
						return;
					}
					
					else if(tempFig instanceof PolylineConnection && me.button == 3){
						if(vosEditor != null)
						{
							vosEditor.setSelection(new StructuredSelection(new PolylineConnection[]{(PolylineConnection)tempFig}));
							SelectionChangedEvent sce = new SelectionChangedEvent(vosEditor, vosEditor.getSelection());
							for(ISelectionChangedListener selectionChangedListener : vosEditor.getSelectionChangedListenerList())
							{
								selectionChangedListener.selectionChanged(sce);
							}
						}
						tempFig = null;
						return;
					}
					
					else if(tempFig instanceof ProgressControl){

							((ProgressControl)tempFig).setProgress(0.5F);
						
							
						return;
					}
					
					while (!(tempFig instanceof Module)){
						try{
							tempFig = (Figure)tempFig.getParent();
						}catch (NullPointerException e){
							return;
						}
					}
					if(vosEditor != null)
					{
						if( tempFig instanceof Module)
						{
							vosEditor.setSelection(new StructuredSelection(new Module[]{(Module)tempFig}));
							SelectionChangedEvent sce = new SelectionChangedEvent(vosEditor, vosEditor.getSelection());
							for(ISelectionChangedListener selectionChangedListener : vosEditor.getSelectionChangedListenerList())
							{
								selectionChangedListener.selectionChanged(sce);
							}
						}
					}
					
					if(me.button == 1){
						mouseButton = 1;
						((Module)tempFig).setSelected();
						xAdjust = me.x - tempFig.getLocation().x;
						yAdjust = me.y - tempFig.getLocation().y;
					}
					else if(me.button == 3){
						mouseButton = 3;
						
					}
					
			}
			
		}
	}

	@Override
	public void mouseReleased(MouseEvent me) {
		if((tempFig != null) && (tempFig instanceof Module)){
			mouseButton = 0;
			((Module)tempFig).setDeselected();
			tempFig = null;
			IEditorPart page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
			if((page != null) && (page instanceof VOSEditor)) ((VOSEditor) page).setDirty(true);
		}
		else if((tempFig != null) && tempConnection == null){
			mouseButton = 0;
			tempFig = null;
			IEditorPart page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
			if((page != null) && (page instanceof VOSEditor)) ((VOSEditor) page).setDirty(true);
		}
		else if(tempFig instanceof IOControl && mouseButton == 3 && tempConnection != null){
			this.getChildren().remove(this.getChildren().size()-1);
			this.getChildren().remove(this.getChildren().size()-1);
			if(this.findFigureAt(me.x, me.y) instanceof IOControl){
				IOControl control = (IOControl)this.findFigureAt(me.x, me.y);
				if(control.getType().equalsIgnoreCase(IModuleControl.TYPE_INPUT)){
					this.setSource((IOControl)tempConnection.getSourceAnchor().getOwner());
					targetAnc = new ChopboxAnchor(control);
					IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
					try {
						handlerService.executeCommand("main.editor.SetTargetHandler", null);
					} catch (Exception e) {
						e.printStackTrace();
					}
					IEditorPart page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
					if((page != null) && (page instanceof VOSEditor)) ((VOSEditor) page).setDirty(true);
				}
			}
			mouseButton = 0;
			tempFig = null;
			tempConnection = null;
			this.repaint();
			
		}
	}

	@Override
	public void mouseDoubleClicked(MouseEvent me) {
		
	}

	@Override
	public void mouseDragged(MouseEvent me) {
		if (tempFig != null && me.x > 250 && mouseButton == 1){
			
			
			if (tempFig instanceof IOControl) {
				Module parentModule = (Module)tempFig.getParent();
				if((me.x- xAdjust)>= parentModule.getLocation().x && (me.x- xAdjust)<= (parentModule.getLocation().x + parentModule.getWidth() - ((IOControl)tempFig).getWidth())
						&& (me.y- yAdjust)>= parentModule.getLocation().y && (me.y- yAdjust)<= (parentModule.getLocation().y + parentModule.getHeight() - ((IOControl)tempFig).getHeight())){
					Point newLoc = new Point(me.x- xAdjust, me.y - yAdjust);
					tempFig.setLocation(newLoc);
					parentModule.getLayoutManager().setConstraint(tempFig, new Rectangle(tempFig.getLocation().x-parentModule.getLocation().x,tempFig.getLocation().y-parentModule.getLocation().y,((IOControl) tempFig).getWidth() , ((IOControl) tempFig).getHeight()));
				}
			}
			else if (tempFig instanceof Module) {
				Point newLoc = new Point(me.x- xAdjust, me.y - yAdjust);
				tempFig.setLocation(newLoc);
				contentsLayout.setConstraint(tempFig, new Rectangle(tempFig.getLocation().x-this.getLocation().x,tempFig.getLocation().y-this.getLocation().y,((Module)tempFig).getWidth(),((Module)tempFig).getHeight()));
			}
			
		}
		else if (tempFig instanceof IOControl && me.x > 250 && mouseButton == 3){
			
			Point p = new Point(me.x, me.y);
			connectionFixPoint.setLocation(p);
			contentsLayout.setConstraint(connectionFixPoint, new Rectangle(me.x-this.getLocation().x, me.y-this.getLocation().y, 1, 1));
			if (tempConnection == null){
				targetAnc = new ChopboxAnchor(connectionFixPoint);
				tempConnection = new PolylineConnection();
				tempConnection.setSourceAnchor(sourceAnc);
				tempConnection.setTargetAnchor(targetAnc);
				this.add(tempConnection);
			}
		}
		
	}

	@Override
	public void mouseEntered(MouseEvent me) {
		
	}

	@Override
	public void mouseExited(MouseEvent me) {
		
	}

	@Override
	public void mouseHover(MouseEvent me) {
		
	}

	@Override
	public void mouseMoved(MouseEvent me) {
		
	}
	
	public String setSource(IOControl m){
		
		if(m.getType().equalsIgnoreCase(IModuleControl.TYPE_OUTPUT) || m.getType().equalsIgnoreCase(IModuleControl.TYPE_BI))
		{
			sourceAnc = new ChopboxAnchor(m);
			return "Sourceanchor successfully set!";
		}
		
		return "Target must be an Output or bidirectional Control!";
		
		
	}
	
	public String setTarget(){
		IOControl targetControl = (IOControl)targetAnc.getOwner();
		
		if(sourceAnc==null)
		{
			return "Please select a SourceAnchor first!";
		}
		
		else if (((IOControl)sourceAnc.getOwner()).getID().equals(targetControl.getID()))
		{
			return "Source and TargetAnchor are not allowed to be the same!";
		}
		
		else 
		{
			if(targetControl.getType().equalsIgnoreCase(IModuleControl.TYPE_INPUT) || targetControl.getType().equalsIgnoreCase(IModuleControl.TYPE_BI)){
				targetAnc = new ChopboxAnchor(targetControl);
				return addConnection() ? "Connection done!" : "Connection already existent!";
			}
			else return "Target must be an Input or bidirectional Control!";
		}
		
	}
	
	public void deleteModule(Module m){
		
		ArrayList<IOControl> controls = new ArrayList<IOControl>();
		
		//remove Connections
		for(int i = 0; i< m.getChildren().size(); i++){
			Object child = m.getChildren().get(i);
			if(child instanceof IOControl){
				controls.add(((IOControl)child));
			}
		}
		for(int i = 0; i< controls.size(); i++){
			IOControl child = controls.get(i);
			
			//remove Connections from Editor
			for(int y = 0; y< ModuleEditor.this.getChildren().size(); y++){
				Object element = ModuleEditor.this.getChildren().get(y);
				if (element instanceof PolylineConnection){
					IOControl sourceOwner = (IOControl)((PolylineConnection)element).getSourceAnchor().getOwner();
					IOControl targetOwner = (IOControl)((PolylineConnection)element).getTargetAnchor().getOwner();
					if(sourceOwner.getID().equals(child.getID())){
						this.remove((PolylineConnection)element);
						y--;
					}
					else if(targetOwner.getID().equals(child.getID())){
						this.remove((PolylineConnection)element);
						y--;
					}
				}
			}
			
			//remove Connection from Connection List
			if(connections.containsKey(child.getID())){
				connections.remove(child.getID());
			}
			Object[] connectionKeys = connections.keySet().toArray();
			for(int y = 0; y < connectionKeys.length; y++){
				UUID key = (UUID) connectionKeys[y];
				for(int z = 0; z < connections.get(key).size(); z++){
					if(connections.get(key).get(z).equals(child.getID())) connections.get(key).remove(z);
				}
			}
			
		}
		//remove Module
		this.remove(m);
	}
	
	public void deleteConnection(PolylineConnection connection){
		
		//remove Connection from Editor
		this.remove(connection);
		//remove Connection from Connection List
		IOControl sourceOwner = (IOControl)connection.getSourceAnchor().getOwner();
		IOControl targetOwner = (IOControl)connection.getTargetAnchor().getOwner();
		if(connections.containsKey(sourceOwner.getID())){
			if (connections.get(sourceOwner.getID()).size() == 1) connections.remove(sourceOwner.getID());
			else {
				for(int y = 0; y < connections.get(sourceOwner.getID()).size(); y++){
					if(connections.get(sourceOwner.getID()).get(y).equals(targetOwner.getID())) connections.get(sourceOwner.getID()).remove(y);
				}
			}
		}
	}
	
	public void importProgram(String filename){
		this.removeAll();
		this.repaint();
		this.connections.clear();
		this.sourceAnc = null;
		this.targetAnc = null;
		XMLParser parser = new XMLParser(filename);
		ArrayList<Object> al = parser.parseXMLProgram();
		//add modules to editor
		for (int i = 0; i<al.size(); i++){
			if (al.get(i) instanceof Module){
				this.add((Module)al.get(i));
				contentsLayout.setConstraint(((Module)al.get(i)), new Rectangle(((Module)al.get(i)).getInitialX()-this.getLocation().x,((Module)al.get(i)).getInitialY()-this.getLocation().y,((Module)al.get(i)).getWidth(),((Module)al.get(i)).getHeight()));
			}
		}
		//search source and target anchors, create connections and add them to the editor
		for (int i = 0; i<al.size(); i++){
			if (al.get(i) instanceof Connection){
				boolean foundAnchors = false;
				for (int j = 0; j < this.getChildren().size() && !foundAnchors; j++){
					if(this.getChildren().get(j) instanceof Module){
						Module tempModule = (Module)this.getChildren().get(j);
						if(tempModule.getID().equals(((Connection)al.get(i)).getSourceModuleID())){
							for(int y = 0; y < tempModule.getChildren().size(); y++){
								if (tempModule.getChildren().get(y) instanceof IOControl){
									IOControl tempControl = (IOControl)tempModule.getChildren().get(y);
									if(tempControl.getID().toString().equals(((Connection)al.get(i)).getSourceID())) this.sourceAnc = new ChopboxAnchor(tempControl);
								}
							}
						}
						else if(tempModule.getID().equals(((Connection)al.get(i)).getTargetModuleID())){
							for(int y = 0; y < tempModule.getChildren().size(); y++){
								if (tempModule.getChildren().get(y) instanceof IOControl){
									IOControl tempControl = (IOControl)tempModule.getChildren().get(y);
									if(tempControl.getID().toString().equals(((Connection)al.get(i)).getTargetID())) this.targetAnc = new ChopboxAnchor(tempControl);
								}
							}
						}
					}
					if(sourceAnc != null && targetAnc != null){
						this.addConnection();
						this.sourceAnc = null;
						this.targetAnc = null;
						foundAnchors = true;
					}
				}
			}
		}
		IEditorPart page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
		if((page != null) && (page instanceof VOSEditor)) ((VOSEditor) page).setDirty(false);
	}

}
